<script setup>
import { reactive, ref } from 'vue';
import SparkMD5 from 'spark-md5';
import axios from 'axios';

// 文件相关定义
const files = reactive({
  fileName: null,
  fileData: null,
});
const fileChunkList = ref([]);
const chunkSize = 4 * 1024 * 1024; // 4MB
const fileChange = es => {
  files.fileName = es.target.files[0].name;
  files.fileData = es.target.files[0];
};

// 文件分片进度
const fileChunkProgressBar = ref(0);
// 处理文件分片
const fileTransfer = async () => {
  let nowChunkSize = 0;
  while (nowChunkSize < files.fileData.size) {
    const nowChunkData = files.fileData.slice(
      nowChunkSize,
      nowChunkSize + chunkSize
    );
    const fileHash = await calculateHash(nowChunkData);
    fileChunkList.value.push({
      chunkName: `${files.fileName}_${fileHash}`,
      chunk: nowChunkData,
    });
    nowChunkSize += chunkSize;
    fileChunkProgressBar.value = Math.round(
      (nowChunkSize / files.fileData.size) * 100
    );
  }
};

// 点击分片文件上传
const uploadFileChunk = () => {
  const chunkList = fileChunkList.value;
  Promise.all(
    chunkList.forEach(async (chunk, index) => {
      uploadChunk(chunk.chunkName, chunk.chunk);
    })
  );
};

// 处理分片文件上传
const uploadChunk = async (chunkName, chunk) => {
  return axios
    .post(
      `http://127.0.0.1:3000/uploadChunk?filename=${files.fileName}&chunkFilename=${chunkName}`,
      chunk,
      {
        headers: {
          'Content-Type': 'application/octet-stream',
        },
      }
    )
    .then(res => {
      console.log(`分片${chunkName}上传成功`);
    })
    .catch(err => {
      console.log(`分片${chunkName}上传失败`);
    });
};

// 文件上传进度
const upLoadProgressBar = ref(0);
// 处理文件上传
const uploadFileList = () => {
  axios
    .post(`http://127.0.0.1:3000/upload/${files.fileName}`, files.fileData, {
      headers: {
        'Content-Type': 'application/octet-stream',
      },
      onUploadProgress: progressEvent => {
        const percentCompleted = Math.round(
          (progressEvent.loaded * 100) / progressEvent.total
        );
        upLoadProgressBar.value = percentCompleted;
      },
    })
    .then(result => {
      console.log(result, '前端上传成功！');
    })
    .catch(err => {
      console.log(err, '前端上传失败！');
    });
};

// 计算文件 hash
const calculateHash = file => {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader(); // 文件读取器

    // 以二进制数据的形式读取文件内容，并将结果存储在 result 属性中，类型为 ArrayBuffer
    fileReader.readAsArrayBuffer(file);
    fileReader.onload = () => {
      // 当读取操作成功完成时触发
      const spark = new SparkMD5.ArrayBuffer(); // 构建hash值对象
      spark.append(fileReader.result); // 添加文件二进制内容
      const hash = spark.end(); // 计算hash值
      resolve(hash);
    };
  });
};
</script>

<template>
  <h6>大文件上传</h6>

  <div>
    <input
      type="file"
      @change="fileChange"
    />
    <button @click="fileTransfer">文件分片处理</button>
    <button @click="uploadFileList">上传文件</button>
    <button @click="uploadFileChunk">上传分片文件</button>

    <br />
    文件分片处理进度：<el-progress :percentage="fileChunkProgressBar" />
    文件上传进度：<el-progress :percentage="upLoadProgressBar" />
  </div>
</template>

<style scoped></style>
